/*
** Classification algorithms for svms
*/

#include "st_classifier.h"
#include <crf_sequence/util.h>
#include <crf_sequence/bp.h>

#include "gzstream.h"
#include "st_wrapper.h"

#include <strings.h>
#include <sstream>
#include <float.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
#include <algorithm>

/*
** A classifier function for the a gaussian process algorithm
*/
STClassifier::STClassifier(boost::shared_array<double> w, GPParameterPtr p) 
//: CRFClassifier(alpha,param)
: m_weights(w), m_param(p)
{
}

EvaluationPtr STClassifier::evaluate(CliqueDetectorPtr cd, int width,
		const std::string &output_file, int num_sentences, int skip, bool viterbi)
{
	EvaluationPtr result(new Evaluation);

	std::string str;
	pp("",true);
	int counter=0;
//	  for (int e=0; e < data->events(); ++e)
	ogzstream outFile(output_file.c_str());
	while (true)
	{
		if (num_sentences >= 0 && counter-skip >= num_sentences)
				break;
		if (counter < skip)
		{
			cd->skip();
			counter++;
			continue;
		}
		if (skip == 0 && (counter % 1) == 0)
		{
			std::stringstream s;
			s << "	" << counter << " instances classified";
			pp(s.str());
		}
		
		CliqueDetector::EventPtr cd_event = cd->next();
		if (!cd_event) break;
		EventPtr event = convert_event(cd_event, width);
		int event_length = event->length;
		int num_labels = cd_event->width;

		// calculate the dot-products
		BP::Array3d node_phi(boost::extents[event_length][event->order][num_labels]);
		BP::Array4d clique_phi(
			boost::extents[event_length-1][event->order][num_labels][num_labels]);
		BP::calculate_dot_products(event, m_weights, node_phi, clique_phi);

		// do max-prodict (viterbi)
		std::vector<size_t> node_labels(event_length);
		if (viterbi)
		{
			BP::LabellingPtr labelling = BP::viterbi(event, node_phi, clique_phi, false);

			// output the node labelling to a file
			int node_index=0;
			for(BP::Labelling::iterator it=labelling->begin();
				it != labelling->end(); ++it)
			{
				for (int n=node_index; n <= it->first; ++n)
				{
					// HACK doesn't word for semi-CRF	
					size_t tag = cd->feature_map()->map_tag
						(cd_event->node_chart->features[n][0]->word,it->second);
					if (output_file != "") outFile << tag << ":1.0" <<  std::endl;
					node_labels[n] = tag;
				}
				node_index = it->first+1;
			}
		}
		else
		{
			BP::Array2d alpha(boost::extents[event_length][num_labels]);
			BP::Array2d beta(boost::extents[event_length][num_labels]);

			BP::forward_backward(event, node_phi, clique_phi, alpha, beta, false);
			double log_partition = -FLT_MAX;
			for (size_t l=0; l<event->nodes[0][event_length-1]->features.size(); ++l)
				log_partition = BP::log_sum(log_partition,alpha[event_length-1][l]);

			std::vector<std::vector<std::pair<double,size_t> > > marginals;
			for (int i=0; i<event_length; ++i)
			{
				size_t width = event->nodes[0][i]->features.size();
				marginals.push_back(std::vector<std::pair<double,size_t> >());
				for (size_t j=0; j<width; ++j)
				{
					std::pair<double,size_t> entry(exp(alpha[i][j]+beta[i][j]-log_partition),j);
					marginals[i].push_back(entry);
				}
				std::sort(marginals[i].begin(), marginals[i].end());
				std::reverse(marginals[i].begin(), marginals[i].end());

				size_t tag = cd->feature_map()->map_tag
					(cd_event->node_chart->features[i][0]->word,marginals[i][0].second);
				if (output_file != "") 
				{
					outFile << tag << ":" << marginals[i][0].first;
					for (size_t x=1; x<width /*&& x<10*/; ++x)
					{
						size_t local_tag = cd->feature_map()->map_tag
							(cd_event->node_chart->features[i][0]->word,marginals[i][x].second);
						outFile << " " << local_tag << ":" << marginals[i][x].first;
					}
					outFile << std::endl;
				}
				node_labels[i] = tag;
			}	
		}
	
		// output the node labelling to a file
		int node_index=0;
		// calculate the accuracy
		node_index = 0;
		for(SequenceCorpusReader::SegmentLabelling::iterator it
			=cd_event->node_chart->gold->begin();
			it != cd_event->node_chart->gold->end(); ++it)
		{
			for (size_t n=it->first.first; n < it->first.second; ++n)
			{
				if(it->second == node_labels[n]) result->tp++;
				else result->fp++;
			}
		}

		counter++;
	}
	if (skip==0) pp("\n",true);

	outFile.close();

	return result;
}

